#include <iostream>
#include <unistd.h>
#include <vector>
#include <cassert>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#define PROCESS_NUMS 5
#define MakeSeed() srand((unsigned long)time(nullptr) ^ getpid() ^ 0x171237 ^ rand() % 1234)
// 函数指针 类型
typedef void (*func_t)();

void downLoadTask()
{
    std::cout << getpid() << ": 下载任务\n"
              << std::endl;
    sleep(1);
}

void ioTask()
{
    std::cout << getpid() << ": IO任务\n"
              << std::endl;
    sleep(1);
}

void flushTask()
{
    std::cout << getpid() << ": 刷新任务\n"
              << std::endl;
    sleep(1);
}

void loadTaskFunc(std::vector<func_t> *out)
{
    // 即使传递的是一个空的vector,但是传过来的地址也是不为空的
    assert(out);
    out->push_back(downLoadTask);
    out->push_back(ioTask);
    out->push_back(flushTask);
}

class subEp
{
public:
    subEp(pid_t subId, int writeId)
        : _subId(subId), _writeFd(writeId)
    {
        char nameBuffer[1024];
        // 参数说明：
        // str：指向要写入的目标字符串缓冲区的指针。
        // size：目标字符串缓冲区的最大字符数，包括空终止符。函数会确保不会写入超过这个长度的字符，以避免缓冲区溢出。
        // format：格式化字符串，用于指定输出的格式，就像 printf 中的格式字符串一样。
        // ...：可变数量的参数，用于与格式字符串中的格式说明符相匹配。
        snprintf(nameBuffer, sizeof nameBuffer, "process-%d[pid(%d) - fd(%d)]", num++, _subId, _writeFd);
        _name = nameBuffer;
    }

public:
    static int num;
    std::string _name;
    pid_t _subId;
    int _writeFd;
};

// 类外面定义静态变量
int subEp::num = 0;

int recvTask(int readFd)
{
    int code = 0;
    // 这里的s代表的是读取到的字节数 . 这里不是waitpid的状态码
    ssize_t s = read(readFd, &code, sizeof code);
    if (s == 4)
    {
        return code;
    }
    else if (s <= 0)
    {
        return -1;
    }
    else
    {
        return 0;
    }
}

void createSubProcess(std::vector<subEp> *subs, std::vector<func_t> &funcMap)
{
    for (int i = 0; i < PROCESS_NUMS; i++)
    {
        int fds[2];
        int n = pipe(fds);

        assert(n == 0);
        (void)n;

        pid_t id = fork();
        if (id == 0)
        {
            close(fds[1]);
            // 子进程通信

            while (true)
            {
                int commandCode = recvTask(fds[0]);
                if (commandCode >= 0 && commandCode < funcMap.size())
                {
                    // 调用函数指针
                    funcMap[commandCode]();
                }
                else if (commandCode == -1)
                {
                    break;
                }
            }
            exit(0);
        }
        close(fds[0]);
        // 父进程
        subEp sub(id, fds[1]);
        subs->push_back(sub);
    }
}

void sendTask(const subEp &process, int taskNum)
{
    std::cout << "send task num: " << taskNum << " send to -> " << process._name << std::endl;

    int n = write(process._writeFd, &taskNum, sizeof taskNum);
    assert(n == sizeof(int));
    (void)n;
}

void loadBlanceContrl(std::vector<subEp> &subs, std::vector<func_t> &funcMap, int count)
{
    int processnum = subs.size();
    int tasknum = funcMap.size();
    bool forever = (count == 0 ? true : false);

    while (true)
    {
        // 查看一共有多少进程
        int subIdx = rand() % processnum;

        // 查看任务索引
        int taskIdx = rand() % tasknum;

        // 发送任务
        sendTask(subs[subIdx], taskIdx);

        sleep(1);

        if (!forever)
        {
            count--;
            if (count == 0)
            {
                break;
            }
        }
    }
     // 一管道的所有write关闭了 ，read 会关闭。 
    for(int i = 0; i < processnum; i++) close(subs[i]._writeFd);
}

void waitProcess(std::vector<subEp> subs)
{
    std::cout<<111<<std::endl;
    int processnum = subs.size();
    for (int i = 0; i < processnum; i++)
    {
        /* code */
        waitpid(subs[i]._subId, nullptr, 0);
        std::cout << "wait sub process success ...: " << subs[i]._subId << std::endl;
    }
}
int main()
{
    MakeSeed();
    std::vector<func_t> funcMap;
    loadTaskFunc(&funcMap);

    std::vector<subEp> subs;

    createSubProcess(&subs, funcMap);

    int taskCnt = 3;

    // 负载均衡
    loadBlanceContrl(subs, funcMap, taskCnt);

    // 3. 回收子进程信息
    waitProcess(subs);

    return 0;
}